#include "Teapot.h"

#include <iostream>
using namespace std;

#include <cmath>

// Static variables and functions

GLuint Teapot::baseList;

const GLdouble Teapot::STEERING_MIN = -30.0;
const GLdouble Teapot::STEERING_MAX = 30.0;

const GLdouble Teapot::SPEED_MIN = -1.0;
const GLdouble Teapot::SPEED_MAX = 3.0;

const GLdouble Teapot::STEERING_OFFSET = -2.0;
const GLdouble Teapot::STEERING_INCREMENT = 2.0;

const GLdouble Teapot::TIRE_FRONT_SPEED = 4.0;
const GLdouble Teapot::TIRE_REAR_SPEED = 5.333;

void Teapot::setup()
{
	Tire::setup();

	baseList = glGenLists(1);
	
	glNewList(baseList, GL_COMPILE);
	
		glPushMatrix();
		glPushAttrib(GL_ALL_ATTRIB_BITS);
	
			// Shiny metal
			GLfloat spec[] = {1.0, 1.0, 1.0, 1.0};
			glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
			glMaterialf(GL_FRONT, GL_SHININESS, 32);
	
			glRotatef(90, 0.0, 1.0, 0.0);
			// For now, at least
			glDisable(GL_TEXTURE_2D);
			glEnable(GL_CULL_FACE);
			glFrontFace(GL_CW);
			mySolidTeapot(6);
			glFrontFace(GL_CCW);
			glDisable(GL_CULL_FACE);
		
		glPopAttrib();
		glPopMatrix();
	
	glEndList();
}

void Teapot::cleanup()
{
	glDeleteLists(baseList, 1);
	
	Tire::cleanup();
}

// Instance methods

Teapot::Teapot(GLdouble x, GLdouble z, GLdouble h)
{
	xPos = x;
	zPos = z;
	heading = h;

	turnAngle = 0;
	color = new GLfloat[4];
	
	setColor(1.0, 1.0, 1.0, 1.0);
	
	speed = 0;
	accelAmount = 0.01;
	brakeAmount = 0.03;
	coastAmount = 0.005;
}

Teapot::~Teapot()
{
	delete[] color;
}

void Teapot::update()
{
	if (dir == LEFT && turnAngle < STEERING_MAX || dir == STRAIGHT && turnAngle < 0.0)
	{
		turnAngle += STEERING_INCREMENT;
	}
	else if (dir == RIGHT && turnAngle > STEERING_MIN || dir == STRAIGHT && turnAngle > 0.0)
	{
		turnAngle -= STEERING_INCREMENT;
	}

	heading -= turnAngle / 30.0 * min(speed, 1.0);
	
	switch(accel)
	{
		case FORWARD:
			if (speed < SPEED_MAX)
			{
				if (speed > 0.0)
				{
					speed += accelAmount;
				}
				else
				{
					speed += brakeAmount;
				}
			}
			break;
		
		case REVERSE:
			if (speed > SPEED_MIN)
			{
				if (speed < 0.0)
				{
					speed -= accelAmount;
				}
				else
				{
					speed -= brakeAmount;
				}
			}
			break;
		
		case COAST:
			if (speed > 0.0)
			{
				speed -= coastAmount;
			}
			break;
	}
	
	GLdouble headingRad = heading * M_PI / 180.0;
	
	xPos += speed * cos(headingRad);
	zPos += speed * sin(headingRad);
	
	GLdouble rotateModifier;
	if (speed > 0.0)
	{
		rotateModifier = min(speed, 1.5);
	}
	else
	{
		rotateModifier = max(speed, -1.5);
	}
	
	rrTire.rotate(-TIRE_REAR_SPEED * rotateModifier);
	rlTire.rotate(-TIRE_REAR_SPEED * rotateModifier);
	frTire.rotate(-TIRE_FRONT_SPEED * rotateModifier);
	flTire.rotate(-TIRE_FRONT_SPEED * rotateModifier);
}

void Teapot::draw() const
{
	glPushMatrix();
		glTranslatef(xPos - STEERING_OFFSET, 0.0, zPos);
		glRotatef(-heading, 0.0, 1.0, 0.0);
		glTranslatef(STEERING_OFFSET, 0.0, 0.0);

		glPushMatrix();
			glColor4fv(color);
			glCallList(baseList);
		
			glPushMatrix();
				glTranslatef(7.0, -4.0, 5.5);
				glScalef(0.75, 0.75, 0.75);
				glRotatef(turnAngle, 0.0, 1.0, 0.0);
				frTire.draw();
			glPopMatrix();
		
			glPushMatrix();
				glTranslatef(7.0, -4.0, -5.5);
				glScalef(0.75, 0.75, -0.75);
				glRotatef(-turnAngle, 0.0, 1.0, 0.0);
				frTire.draw();
			glPopMatrix();
		
			glPushMatrix();
				glTranslatef(-6.0, -3.5, 5.0);
				rrTire.draw();
			glPopMatrix();
		
			glPushMatrix();
				glTranslatef(-6.0, -3.5, -5.0);
				glScalef(1.0, 1.0, -1.0);
				rlTire.draw();
			glPopMatrix();
		glPopMatrix();
	
	glPopMatrix();
}

GLdouble Teapot::getX() const
{
	return xPos;
}

GLdouble Teapot::getZ() const
{
	return zPos;
}

GLdouble Teapot::getHeading() const
{
	return heading;
}

void Teapot::turn(Teapot::Direction d)
{
	dir = d;
}

void Teapot::accelerate(Teapot::Acceleration a)
{
	accel = a;
}

void Teapot::setTurnAngle(GLdouble a)
{
	turnAngle = a;
}

void Teapot::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
	color[0] = r;
	color[1] = g;
	color[2] = b;
	color[3] = a;
}

